package top.infopub.mgc.util;


import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 文件夹Zip压缩   </br>
 * 此类功能：</br>
 * 为向特定邮箱发送压缩的投资合同使用</br>
 * 因为某些邮箱邮件附件不能超过9M </br>
 * 而一个标的的投资合同压缩后可能超过9M</br>
 * 这里每《参数：SPLIT》个pdf压缩成一个zip(保证不超过9M)</br>
 * 命名projectIdSign-ATT00 …… projectIdSign-ATT99
 * @author Awoke
 * @version 2018年4月26日
 * @see FileZipsUtil
 * @since
 */
public final class FileZipsUtil {

    private static final Logger log = LoggerFactory.getLogger(FileZipsUtil.class);

    /**
     * 每N个文件压缩成1个zip
     */
    private static final int SPLIT = 10;

    /**
     * 投资合同文件夹Zip压缩  
     * @param srcfile
     * @return 生成的zip文件路径集合
     * @see
     */
    public static List<String> zipInvestAtts(File srcfile) {
        List<String> attsArray = new ArrayList<>();
        // 按照SPLIT分组成多个list
        List<List<String>> target = split(listPdfs(srcfile));
        // zip索引 
        int index = 0;
        for (List<String> lookUpPdfs : target) {
            // 从00开始
            attsArray.add(zip(srcfile, lookUpPdfs,
                StringUtils.leftPad(String.valueOf(index), 2, "0")));
            index++ ;
        }
        return attsArray;
    }

    /**
     * 压缩文件夹夹 
     * @param srcfile 需要压缩的源文夹 
     * @param lookUpPdfs 需要压缩pdf文件名集合
     * @param fileIndex zip文件后缀
     * @return 返回srcFile同目录下的同名.zip FileName
     * @see
     */
    private static String zip(File srcfile, List<String> lookUpPdfs, String fileIndex) {
        if (srcfile == null) {
            return null;
        }
        ZipOutputStream out = null;
        File targetFile = null;
        try {
            targetFile = new File(getTargetFilePath(srcfile, fileIndex));
            out = new ZipOutputStream(new FileOutputStream(targetFile));
            // 添加对应的文件Entry
            addEntry("", srcfile, out, lookUpPdfs);
            return targetFile.getPath();
        }
        catch (Exception e) {
            log.error("FileZipUtil#zip occurs exception", e);
        }
        finally {
            IOUtils.closeQuietly(out);
        }
        return null;
    }

    /**
     * 返回srcFile同目录下的.zip文件名
     * @param srcFile
     * @param fileIndex
     * @return 
     * @see
     */
    private static String getTargetFilePath(File srcfile, String fileIndex)
        throws Exception {
        String targetFilePath = srcfile.getPath() + "-ATT" + fileIndex + ".zip";
        // 先删除原zip文件
        File targetFile = new File(targetFilePath);
        if (targetFile.exists()) {
            FileUtils.forceDelete(targetFile);
        }
        return targetFilePath;
    }

    /**
     * 扫描添加文件Entry
     * @param base   基路径
     * @param source 源文件
     * @param zos    zip文件输出流
     * @param lookUpPdfs  筛选pdf
     * @throws IOException
     */
    private static void addEntry(String base, File source, ZipOutputStream zos,
                                 List<String> lookUpPdfs)
        throws IOException {
        // 按目录分级，形如：/aaa/bbb.pdf
        String entry = base + source.getName();
        if (source.isDirectory()) {
            for (File file : source.listFiles()) {
                // 递归列出目录下的所有文件，添加文件Entry
                addEntry(entry + "/", file, zos, lookUpPdfs);
            }
        }
        else {
            String fileName = FilenameUtils.getName(source.getPath());

            // 目前是只压缩文件夹下[目标pdf文件]
            if (lookUpPdfs.contains(fileName)) {
                FileInputStream fis = null;
                BufferedInputStream bis = null;
                try {
                    byte[] buffer = new byte[1024 * 10];
                    fis = new FileInputStream(source);
                    bis = new BufferedInputStream(fis, buffer.length);
                    int read = 0;
                    zos.putNextEntry(new ZipEntry(entry));
                    while ((read = bis.read(buffer, 0, buffer.length)) != -1) {
                        zos.write(buffer, 0, read);
                    }
                    zos.closeEntry();
                }
                finally {
                    IOUtils.closeQuietly(fis);
                    IOUtils.closeQuietly(bis);
                }
            }
        }
    }

    /**
     * 文件夹中含有的pdf
     * @param srcfile
     * @return 
     * @see
     */
    private static List<String> listPdfs(File srcfile) {
        List<String> pdfs = new ArrayList<>();
        for (File file : srcfile.listFiles()) {
            // 只筛选pdf文件
            if (FilenameUtils.getExtension(file.getPath()).equals("pdf")) {
                pdfs.add(file.getName());
            }
        }
        return pdfs;
    }

    /**
     * 按照SPLIT分批处理
     * @param fileNameList 
     * @see
     */
    private static List<List<String>> split(List<String> fileNameList) {
        List<List<String>> target = new ArrayList<>();
        
        // 分批处理
        if (CollectionUtils.isNotEmpty(fileNameList)) {
            int size = fileNameList.size();
            // 判断是否有必要分批
            if (SPLIT < size) {
                // 分批数
                int part = size / SPLIT;
                for (int i = 0; i < part; i++ ) {
                    List<String> listPage = fileNameList.subList(0, SPLIT);
                    List<String> toAdd= new ArrayList<>(listPage);
                    target.add(toAdd);
                    // 剔除
                    listPage.clear();
                }
                // 表示最后剩下的数据
                if (!fileNameList.isEmpty()) {
                    target.add(fileNameList);
                }
            }
            // 不需要分批处理
            else {
                target.add(fileNameList);
            }
        }
        return target;
    }

    public static void main(String[] args) {
        System.out.println(zipInvestAtts(new File("E:/opt/znjf/data/pdf/0AC84C6082F645FAB63F775360D2A954Sign")));
        //String pdf = "E:/opt/znjf/data/pdf/0AC84C6082F645FAB63F775360D2A954Sign";
        //System.out.println(FilenameUtils.getFullPathNoEndSeparator(pdf));
        //System.out.println(FilenameUtils.getName(pdf));
    }
}
